﻿/***************************************************************************
 *   Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "pch.h"
#ifndef _PreComp_
# include <boost_signals2.hpp>
# include <boost_bind_bind.hpp>
# include <QtWidgets/QApplication>
# include <QRegExp>
# include <QEvent>
# include <QtGui/QCloseEvent>
# include <QtWidgets/QMdiSubWindow>
//# include <QPrinter>
//# include <QPrinterInfo>
#endif

#include <QtWidgets/QToolBar>
#include <QtWidgets/QToolButton>
#include <QtWidgets/qstatusbar.h>
#include <App/Document.h>

#include "MDIView.h"

#include "Command.h"
#include "SketchCmd.h"
#include "Gui/Application.h"

#include "ViewProviderDocumentObject.h"
#include <Gui/Document.h>
// 流动布局类（来自Qt官方示例）
class FlowLayout : public QLayout {
public:
	explicit FlowLayout(QWidget* parent = nullptr, int margin = 0, int spacing = -1)
		: QLayout(parent) {
		setContentsMargins(margin, margin, margin, margin);
		setSpacing(spacing);
	}

	~FlowLayout() {
		//clear();
	}

	void addItem(QLayoutItem* item) override {
		itemList.append(item);
	}

	int count() const override {
		return itemList.size();
	}

	QLayoutItem* itemAt(int index) const override {
		return itemList.value(index);
	}

	QLayoutItem* takeAt(int index) override {
		return (index >= 0 && index < itemList.size()) ? itemList.takeAt(index) : nullptr;
	}

	Qt::Orientations expandingDirections() const override {
		return Qt::Horizontal | Qt::Vertical;
	}

	bool hasHeightForWidth() const override {
		return true;
	}

	int heightForWidth(int width) const override {
		return doLayout(QRect(0, 0, width, 0), true);
	}

	QSize sizeHint() const override {
		return minimumSize();
	}

	QSize minimumSize() const override {
		QSize size;
		for (const QLayoutItem* item : itemList) {
			size = size.expandedTo(item->minimumSize());
		}
		return size + QSize(2 * margin(), 2 * margin());
	}

	void setGeometry(const QRect& rect) override {
		QLayout::setGeometry(rect);
		doLayout(rect, false);
	}

private:
	int doLayout(const QRect& rect, bool testOnly) const {
		int x = rect.x();
		int y = rect.y();
		int lineHeight = 0;

		for (QLayoutItem* item : itemList) {
			QWidget* widget = item->widget();
			if (!widget) continue;

			int spaceX = spacing();
			int spaceY = spacing();
			int nextX = x + item->sizeHint().width() + spaceX;
			if (nextX - spaceX > rect.right() && lineHeight > 0) {
				x = rect.x();
				y += lineHeight + spaceY;
				nextX = x + item->sizeHint().width() + spaceX;
				lineHeight = 0;
			}

			if (!testOnly) {
				widget->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
			}

			x = nextX;
			lineHeight = qMax(lineHeight, item->sizeHint().height());
		}
		return y + lineHeight - rect.y();
	}

	QList<QLayoutItem*> itemList;
};

using namespace Gui;
namespace bp = boost::placeholders;

TYPESYSTEM_SOURCE_ABSTRACT(Gui::MDIView,Gui::BaseView)

MDIView::MDIView(Gui::Document* pcDocument,QWidget* parent, Qt::WindowFlags wflags)
  : QMainWindow(parent,wflags)
  , BaseView(pcDocument)
  
  , currentMode(Child)
  , wstate(Qt::WindowNoState)
  , ActiveObjects(pcDocument)
{
    setAttribute(Qt::WA_DeleteOnClose);

    if (pcDocument)
    {
        connectDelObject = pcDocument->signalDeletedObject.connect
          (boost::bind(&ActiveObjectList::objectDeleted, &ActiveObjects, bp::_1));
        assert(connectDelObject.connected());
    }
    // 创建菜单栏
    QMenuBar *childMenuBar = new QMenuBar(this);
    setMenuBar(childMenuBar);

    QMenu *childFileMenu = childMenuBar->addMenu("File");
    QAction *childExitAction = childFileMenu->addAction("Exit");
    connect(childExitAction, &QAction::triggered, this, &MDIView::close);
    QToolBar *childToolBar = new QToolBar("Tools", this);
    addToolBar(childToolBar);
    // 创建容器和流动布局
    QWidget* container = new QWidget;
    FlowLayout* flowLayout = new FlowLayout(container, 0, 2); // 无边距，最小间距
    container->setLayout(flowLayout);
    for(const auto& cmd : sketch_create_cmds){
        QToolButton* button = new QToolButton;
        button->setText(QString(cmd));
        button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
        button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        flowLayout->addWidget(button);
        connect(button, &QToolButton::clicked, this, [cmd]() {
            // QMessageBox::information(nullptr, str1, "Child action triggered");
            Gui::Application::Instance->commandManager().getCommandByName(cmd)->invoke(0, Gui::Command::TriggerAction);
        });
    }
    // 将容器添加到工具栏
    childToolBar->addWidget(container);

    // 设置布局属性
    container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    childToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    childToolBar->setFixedHeight(98); // 略大于按钮高度（30px + 边距）

    // 禁用工具栏拖动和浮动
    childToolBar->setMovable(false);
    childToolBar->setFloatable(false);
    // 创建状态栏
    QStatusBar *childStatusBar = new QStatusBar(this);
    childStatusBar->showMessage("Child Window Ready");
    setStatusBar(childStatusBar);
}
MDIView::~MDIView(){}

void MDIView::deleteSelf()
{
    // When using QMdiArea make sure to remove the QMdiSubWindow
    // this view is associated with.
    //
    // #0001023: Crash when quitting after using Windows > Tile
    // Use deleteLater() instead of delete operator.
    QWidget* parent = this->parentWidget();
    if (qobject_cast<QMdiSubWindow*>(parent)) {
        // https://forum.freecadweb.org/viewtopic.php?f=22&t=23070
        parent->close();
    }
    else {
        this->close();
    }

    // detach from document
    if (_pcDocument)
        onClose();
    _pcDocument = nullptr;
}



void MDIView::setOverrideCursor(const QCursor& c)
{
    Q_UNUSED(c);
}

void  MDIView::restoreOverrideCursor()
{
}

void MDIView::onRelabel(Gui::Document *pDoc)
{
    if (!bIsPassive) {
        // Try to separate document name and view number if there is one
        QString cap = windowTitle();
        // Either with dirty flag ...
        QRegExp rx(QLatin1String("(\\s\\:\\s\\d+\\[\\*\\])$"));
        int pos = rx.lastIndexIn(cap);
        if (pos == -1) {
            // ... or not
            rx.setPattern(QLatin1String("(\\s\\:\\s\\d+)$"));
            pos = rx.lastIndexIn(cap);
        }
        if (pos != -1) {
            cap = QString::fromUtf8(pDoc->getDocument()->Label.getValue());
            cap += rx.cap();
            setWindowTitle(cap);
        }
        else {
            cap = QString::fromUtf8(pDoc->getDocument()->Label.getValue());
            cap = QString::fromLatin1("%1[*]").arg(cap);
            setWindowTitle(cap);
        }
    }
}

void MDIView::viewAll()
{
}

/// receive a message
bool MDIView::onMsg(const char* pMsg,const char** ppReturn)
{
    Q_UNUSED(pMsg);
    Q_UNUSED(ppReturn);
    return false;
}

bool MDIView::onHasMsg(const char* pMsg) const
{
    Q_UNUSED(pMsg);
    return false;
}

bool MDIView::canClose()
{
    if (getAppDocument() && getAppDocument()->testStatus(App::Document::TempDoc))
        return true;

    if (!bIsPassive && getGuiDocument() && getGuiDocument()->isLastView()) {
        this->setFocus(); // raises the view to front
        return (getGuiDocument()->canClose(true,true));
    }

    return true;
}

void MDIView::closeEvent(QCloseEvent *e)
{
    if (canClose()) {
        e->accept();
        if (!bIsPassive) {
            // must be detached so that the last view can get asked
            Document* doc = this->getGuiDocument();
            if (doc && !doc->isLastView())
                doc->detachView(this);
        }

        // Note: When using QMdiArea we must not use removeWindow()
        // because otherwise the QMdiSubWindow will lose its parent
        // and thus the notification in QMdiSubWindow::closeEvent of
        // other mdi windows to get maximized if this window
        // is maximized will fail.
        // This odd behaviour is caused by the invocation of
        // d->mdiArea->removeSubWindow(parent) which we must let there
        // because otherwise other parts don't work as they should.
        QMainWindow::closeEvent(e);
    }
    else
        e->ignore();
}

void MDIView::windowStateChanged( MDIView* )
{
}

void MDIView::print(QPrinter* printer)
{
    Q_UNUSED(printer);
    std::cerr << "Printing not implemented for " << this->metaObject()->className() << std::endl;
}

void MDIView::print()
{
    std::cerr << "Printing not implemented for " << this->metaObject()->className() << std::endl;
}

void MDIView::printPdf()
{
    std::cerr << "Printing PDF not implemented for " << this->metaObject()->className() << std::endl;
}

void MDIView::printPreview()
{
    std::cerr << "Printing preview not implemented for " << this->metaObject()->className() << std::endl;
}

void MDIView::savePrinterSettings(QPrinter* printer){}

void MDIView::restorePrinterSettings(QPrinter* printer){}

QStringList MDIView::undoActions() const
{
    QStringList actions;
    Gui::Document* doc = getGuiDocument();
    if (doc) {
        std::vector<std::string> vecUndos = doc->getUndoVector();
        for (std::vector<std::string>::iterator i = vecUndos.begin(); i != vecUndos.end(); ++i) {
            actions << QCoreApplication::translate("Command", i->c_str());
        }
    }

    return actions;
}

QStringList MDIView::redoActions() const
{
    QStringList actions;
    Gui::Document* doc = getGuiDocument();
    if (doc) {
        std::vector<std::string> vecRedos = doc->getRedoVector();
        for (std::vector<std::string>::iterator i = vecRedos.begin(); i != vecRedos.end(); ++i) {
            actions << QCoreApplication::translate("Command", i->c_str());
        }
    }

    return actions;
}

QSize MDIView::minimumSizeHint () const
{
    return QSize(400, 300);
}

void MDIView::changeEvent(QEvent *e){}

#if defined(Q_WS_X11)
// To fix bug #0000345 move function declaration to here
extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_x11.cpp
#endif

void MDIView::setCurrentViewMode(ViewMode mode)
{
    switch (mode) {
        // go to normal mode
        case Child:
            {
                if (this->currentMode == FullScreen) {
                    showNormal();
                    setWindowFlags(windowFlags() & ~Qt::Window);
                }
                else if (this->currentMode == TopLevel) {
                    this->wstate = windowState();
                    setWindowFlags( windowFlags() & ~Qt::Window );
                }

                if (this->currentMode != Child) {
                    this->currentMode = Child;
                    // getMainWindow()->addWindow(this);
                    // getMainWindow()->activateWindow();
                    update();
                }
            }   break;
        // go to top-level mode
        case TopLevel:
            {
                if (this->currentMode == Child) {
                    if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
                        // getMainWindow()->removeWindow(this,false);
                    setWindowFlags(windowFlags() | Qt::Window);
                    setParent(nullptr, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
                                 Qt::WindowMinMaxButtonsHint);
                    if (this->wstate & Qt::WindowMaximized)
                        showMaximized();
                    else
                        showNormal();

#if defined(Q_WS_X11)
                    //extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_x11.cpp
                    qt_x11_wait_for_window_manager(this);
#endif
                    activateWindow();
                }
                else if (this->currentMode == FullScreen) {
                    if (this->wstate & Qt::WindowMaximized)
                        showMaximized();
                    else
                        showNormal();
                }

                this->currentMode = TopLevel;
                update();
            }   break;
        // go to fullscreen mode
        case FullScreen:
            {
                if (this->currentMode == Child) {
                    if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
                        // getMainWindow()->removeWindow(this,false);
                    setWindowFlags(windowFlags() | Qt::Window);
                    setParent(nullptr, Qt::Window);
                    showFullScreen();
                }
                else if (this->currentMode == TopLevel) {
                    this->wstate = windowState();
                    showFullScreen();
                }

                this->currentMode = FullScreen;
                update();
            }   break;
    }
}
